#!/usr/bin/wish

#c must be a list of config requests
#text request : text "text label" "default value"
#text return : "text value"
#option request : option "option label" {list of options}
#option return : "selected index"


set getConfigTopName "..."
set getConfigConfigInfo ""
set configWindowDebug 1
set configParent "."
proc getConfig { w c } {
  global returnVar
  global getConfigTopName
  global getConfigConfigInfo
  global configWindowDebug
  global configParent
  global scrollTop
  global totalScroll
  set getConfigTopName $w
  set getConfigConfigInfo $c
  toplevel $w
  set scrollWidth 20
  frame $w.topFrame
  place $w.topFrame -relwidth 1.0 -width -$scrollWidth -relheight 1.0 -x 0 -y 0
  set w "$w.topFrame"
  set scrollName $getConfigTopName.vScroll
  set buttonName $getConfigTopName.bOk
  scrollbar $scrollName -command "getConfigDoScroll $w $scrollName"
  set nextFrameTop 0
  set widestConfig 0
  set ti 0
  set oi 0
  set cbi 0
  if {$configWindowDebug} {
    log "getConfig $w $c"
    log "config [llength $c] items"
  }
  foreach ci $c {
    set configType [lindex $ci 0]
    if {$configWindowDebug} {
      log "ci=$ci configType=$configType"
    }
    switch $configType {
      text  {
              frame $w.ft$ti
              set labelText [lindex $ci 1]
              set defaultText [lindex $ci 2]
              if {$configWindowDebug} {
                log "config text $labelText $defaultText"
              }
              label $w.ft$ti.el -text $labelText -anchor w -justify left
              if {([string compare $labelText "Password"] != 0) && ![string match -nocase "*password*" $labelText]} {
                entry $w.ft$ti.en
              } else {
                entry $w.ft$ti.en -show *
              }
              $w.ft$ti.en insert 0 $defaultText
              pack $w.ft$ti.el $w.ft$ti.en -side left -in $w.ft$ti
              #pack $w.ft$ti -side top -anchor w
              #place $w.ft$ti -x 0 -relwidth 1.0 -y $nextFrameTop
              place $w.ft$ti -x 0 -y $nextFrameTop
              update
              set widestConfig [configMax $widestConfig [winfo width $w.ft$ti]]
              incr nextFrameTop [winfo height $w.ft$ti]
              incr ti
            }
      option  {
                frame $w.fo$oi
                global ${w}_fo${oi}
                set optionsText [lindex $ci 1]
                set optionsList [lindex $ci 2]
                set optionsDefault [lindex $ci 3]
                label $w.fo$oi.l -text $optionsText -anchor w -justify left
                pack $w.fo$oi.l -side top -in $w.fo$oi
                set oii 0
                foreach optText $optionsList {
                  radiobutton $w.fo$oi.foi$oii -text $optText -variable "${w}_fo${oi}" -value $oii -anchor w
                  pack $w.fo$oi.foi$oii -side top -in $w.fo$oi -anchor w
                  if {$optionsDefault == $oii} {
                    $w.fo$oi.foi$oii select
                  }
                  incr oii
                }
                #pack $w.fo$oi -side top -anchor w
                #place $w.fo$oi -x 0 -relwidth 1.0 -y $nextFrameTop
                place $w.fo$oi -x 0 -y $nextFrameTop
                update
                incr nextFrameTop [winfo height $w.fo$oi]
                set widestConfig [configMax $widestConfig [winfo width $w.fo$oi]]
                incr oi
              }
      checkBox  {
                  frame $w.fc$cbi
                  set checkText [lindex $ci 1]
                  set checkState [lindex $ci 2]
                  checkbutton $w.fc$cbi.optionCheck$cbi -text $checkText -anchor w
                  if {$configWindowDebug} {
                    log "should look at $w.fc$cbi.optionCheck$cbi"
                  }
                  global optionCheck$cbi
                  set optionCheck$cbi $checkState
                  pack $w.fc$cbi.optionCheck$cbi -side left -in $w.fc$cbi
                  update
                  #pack $w.fc$cbi -side top -anchor w
                  #place $w.fc$cbi -x 0 -relwidth 1.0 -y $nextFrameTop
                  place $w.fc$cbi -x 0 -y $nextFrameTop
                  update
                  set widestConfig [configMax $widestConfig [winfo width $w.fc$cbi.optionCheck$cbi]]
                  incr nextFrameTop [winfo height $w.fc$cbi]
                  incr cbi
                }
      default {
                if {$configWindowDebug} {
                  log "found default config request...not good ($configType)"
                }
              }
    }
  }
  button $buttonName -text "OK" -command { set returnVar [getConfigReturn]; destroy $getConfigTopName}
  bind $getConfigTopName <Return> { set returnVar [getConfigReturn]; destroy $getConfigTopName }
  bind $getConfigTopName <Escape> { set returnVar 0; destroy $getConfigTopName  }
  #bind $getConfigTopName <?> "log \"[place info $w]\n[place slaves $w] \""
  #bind $getConfigTopName <Shift-1> "foreach i [list [place slaves $w]] {incrWinYPlace \$i 50}"
  #bind $getConfigTopName <Shift-2> "foreach i [list [place slaves $w]] {incrWinYPlace \$i -50}"
  #bind $getConfigTopName <1> "incrWinYPlace $w 50"
  #bind $getConfigTopName <2> "incrWinYPlace $w -50"
  #pack $w.bok -side bottom
  place $buttonName -x 0 -rely 1.0
  update
  place configure $buttonName -y -[winfo height $buttonName]
  set configHeight $nextFrameTop
  incr nextFrameTop [winfo height $buttonName]
  place $scrollName -relx 1.0 -x -$scrollWidth -width $scrollWidth -relheight 1.0 -height -[winfo height $buttonName] -y 0
  place configure $w -height -[winfo height $buttonName]

  focus $getConfigTopName
  grab $getConfigTopName
  wm transient $getConfigTopName $configParent
  wm protocol $getConfigTopName WM_DELETE_WINDOW { set returnVar 0; destroy $getConfigTopName  }
  raise $getConfigTopName
  set oldGeo [wm geometry $getConfigTopName]
  regexp {([0-9]+)x([0-9]+)} $oldGeo junk oldW oldH
  if {$nextFrameTop - 40 > [winfo screenheight .]} {
    set newH [expr "[winfo screenheight .] - 40"]
  } else {
    set newH $nextFrameTop
  }
  #log "oldW $oldW widestConfig $widestConfig iww [winfo width $w]"
  set newW [configMax $oldW [expr {$widestConfig + $scrollWidth}]]
  wm geometry $getConfigTopName "${newW}x${newH}"
  $scrollName set 0.0 [expr {$newH / "$nextFrameTop.0"}]
  set scrollTop 0
  set totalScroll $nextFrameTop.0
  tkwait window $getConfigTopName
  return $returnVar
}

proc configMax { a b } {
  if {$a >= $b} {
    return $a
  } else {
    return $b
  }
}

proc incrAllChildren { w i } {
  foreach child [place slaves $w] {
    incrWinYPlace $child $i
  }
}

proc incrWinYPlace { w i } {
  array set pi [place info $w]
  place configure $w -y [expr {$pi(-y) - $i}]
}

proc getConfigDoScroll { w sw args } {
  global scrollTop totalScroll
  #log "ds: $w $args"
  set cmd [lindex $args 0]
  switch $cmd {
    moveto {
      set newScrollTop [expr "int([lindex $args 1] * $totalScroll)"]
      if {$newScrollTop < 0} {
        set newScrollTop 0
      }
      if {[expr "$newScrollTop + [winfo height $w]"] > $totalScroll} {
        set newScrollTop [expr "int($totalScroll - [winfo height $w])"]
      }

      set scrollDelta [expr {$newScrollTop - $scrollTop}]
      incrAllChildren $w $scrollDelta
      incr scrollTop $scrollDelta
      $sw set [expr {$scrollTop/ $totalScroll}] [expr "[expr "$scrollTop + [winfo height $w]"] / $totalScroll"]
    }
    scroll {
      if {[lindex $args 2] == "units"} {
        set scrollMultiple 20
      } else {
        set scrollMultiple [winfo height $w]
      }
      set scrollDelta [expr "$scrollMultiple * [lindex $args 1]"]
      if {[expr "$scrollTop + $scrollDelta + [winfo height $w]"] > $totalScroll} {
        set scrollDelta [expr "int(($totalScroll - [winfo height $w]) - $scrollTop)"]
        incrAllChildren $w $scrollDelta
        incr scrollTop $scrollDelta
        $sw set [expr {$scrollTop/ $totalScroll}] [expr "[expr "$scrollTop + [winfo height $w]"] / $totalScroll"]
      } elseif {[expr {$scrollTop + $scrollDelta}] < 0} {
        set scrollDelta -$scrollTop
        incrAllChildren $w $scrollDelta
        incr scrollTop $scrollDelta
        $sw set [expr {$scrollTop/ $totalScroll}] [expr "[expr "$scrollTop + [winfo height $w]"] / $totalScroll"]
      } else {
        incrAllChildren $w $scrollDelta
        incr scrollTop $scrollDelta
        $sw set [expr {$scrollTop/ $totalScroll}] [expr "[expr "$scrollTop + [winfo height $w]"] / $totalScroll"]
      }
    }
  }
}
proc getConfigReturn { } {
  global getConfigTopName
  global getConfigConfigInfo
  global configWindowDebug
  if {$configWindowDebug} {
    log "getting config return "
  }
  #set w $getConfigTopName
  set w "$getConfigTopName.topFrame"
  set c $getConfigConfigInfo
  set ti 0
  set oi 0
  set cbi 0
  set returnVal [list]
  if {$configWindowDebug} {
    log "checking for configs for $c"
  }
  foreach ci $c {
    set configType [lindex $ci 0]
    switch $configType {
      text  {
              lappend returnVal [$w.ft$ti.en get]
              incr ti
            }
      option  {
                set varName "${w}_fo${oi}"
                global $varName

                lappend returnVal [set "$varName"]
                incr oi
              }
      checkBox  {
                  set varName "optionCheck$cbi"
                  global $varName
                  lappend returnVal [set "$varName"]
                  incr cbi
                }
      default {
              }
    }
  }
  return $returnVal
}

if {[info ex argv0] && [file tail [info script]] == [file tail $argv0]} {
  text .txtLog -width 80 -height 20
  place .txtLog -relwidth 1.0  -relheight 1.0 -x 0 -y 0
  wm geometry . "600x600"

  proc log { msg } {
    .txtLog insert end "$msg\n"
    .txtLog see end
    update; update idletasks
  }
  log "testing config window"
  set getConfigTopName "ConfigWindow Test"
  #set configParam [list [list text "Sample text" "default value"] [list option "pick a color" [list red green blue]]]
  set configParam [list]
  for {set i 0} {$i < 2} {incr i} {
    lappend configParam [list text "longer caption$i" "default$i"]
  }
  for {set i 0} {$i < 2} {incr i} {
    lappend configParam [list option "caption$i" [list opt0 opt1 opt2]]
  }
  set result [getConfig ".configWindow" $configParam]
  log $result
}

